package com.caine.threadpool;


import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.ExecutionException;
import java.util.function.*;

/**
 * 没有指定Executor的方法会使用ForkJoinPool.commonPool() 作为它的线程池执行异步代码。如果指定线程池，则使用指定的线程池运行。
 * 以下所有的方法都类同。
 */
public class CompletableFutureTest {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        Runnable runnable = () -> System.out.println(Thread.currentThread().getName());
        CompletableFuture<Void> future = CompletableFuture.runAsync(runnable);
        // 无返回值
        future.get();
        // 有返回值
        Supplier<Integer> supplier = () -> 2;
        CompletableFuture<Integer> supplyFuture = CompletableFuture.supplyAsync(supplier);
        Integer retVal = supplyFuture.get();
        System.out.println("retVal = " + retVal);
        /*
            whenComplete 和 whenCompleteAsync 的区别：
            whenComplete：是执行当前任务的线程执行继续执行 whenComplete 的任务。
            whenCompleteAsync：是执行把 whenCompleteAsync 这个任务继续提交给线程池来进行执行。
         */
        supplyFuture.whenComplete((ret, action) -> System.out.println("whenComplete ThreadName:"+Thread.currentThread().getName()+" ret:" + ret));
        supplyFuture.whenCompleteAsync((ret, action) -> System.out.println("whenCompleteAsync ThreadName:"+Thread.currentThread().getName()+" ret:" + ret));
        supplyFuture.exceptionally(throwable -> {
            System.out.println("throwable = " + throwable);
            return null;
        });
        supplyFuture.get();
//      thenApply方法-> 第二个任务依赖第一个任务的结果。
        CompletableFuture<Integer> supplyFutureThen = CompletableFuture.supplyAsync(supplier);
        supplyFutureThen = supplyFutureThen.thenApply(i -> i + 3);
        Integer retSupplyFutureThen = supplyFutureThen.get();
        System.out.println("retSupplyFutureThen = " + retSupplyFutureThen);
        /*
        handle-> 是执行任务完成时对结果的处理。
        handle 方法和 thenApply 方法处理方式基本一样。不同的是 handle 是在任务完成后再执行，还可以处理异常的任务。thenApply 只可以执行正常的任务，任务出现异常则不执行 thenApply 方法。
        在 handle 中可以根据任务是否有异常来进行做相应的后续处理操作。而 thenApply 方法，如果上个任务出现错误，则不会执行 thenApply 方法。
        */
        CompletableFuture<Integer> supplyFutureHandle = CompletableFuture.supplyAsync(supplier);
        BiFunction<Integer,Throwable,Integer> supplyFutureBiFunction = (i, e) -> {
            int ret = -1;
            if(e == null) {
                ret = i + 4;
            }else{
                System.out.println("e.getMessage() = " + e.getMessage());
            }
            return ret;
        };
        supplyFutureHandle = supplyFutureHandle.handle(supplyFutureBiFunction);
        Integer retSupplyFutureHandle = supplyFutureHandle.get();
        System.out.println("retSupplyFutureHandle = " + retSupplyFutureHandle);

//      thenAccept-> 消费处理结果接收任务的处理结果，并消费处理，无返回结果。
        CompletableFuture<Integer> supplyFutureThenAccept = CompletableFuture.supplyAsync(supplier);
        Consumer<Integer> supplyFutureConsumer = i-> System.out.println("(i+100) = " + (i+100));
        supplyFutureThenAccept.thenAccept(supplyFutureConsumer);
//      thenRun方法-> 不关心任务的处理结果。只要上面的任务执行完成，就开始执行 thenAccept 。
//      并不会把计算的结果传给 thenRun 方法。只是处理完任务后，执行 thenAccept 的后续操作
        CompletableFuture<Integer> supplyFutureThenRun = CompletableFuture.supplyAsync(supplier);
        Runnable thenRunAbel = () -> System.out.println("thenRun.");
        supplyFutureThenRun.thenRun(thenRunAbel);
//      thenCombine合并任务-> 会把 两个 CompletionStage 的任务都执行完成后，把两个任务的结果一块交给 thenCombine 来处理。
        Supplier<Integer> supplier01 = () -> 1;
        Supplier<Integer> supplier02 = () -> 2;
        CompletableFuture<Integer> supplyFuture01 = CompletableFuture.supplyAsync(supplier01);
        CompletableFuture<Integer> supplyFuture02 = CompletableFuture.supplyAsync(supplier02);
        CompletableFuture<Integer> thenCommbineCf = supplyFuture01.thenCombine(supplyFuture02, (s01, s02) -> s01 + s02);
        System.out.println("thenCommbineCf.get() = " + thenCommbineCf.get());
//      thenAcceptBoth 当两个CompletionStage都执行完成后，把结果一块交给thenAcceptBoth来进行消耗
        CompletableFuture<Void> thenAcceptBothCf = supplyFuture01.thenAcceptBoth(supplyFuture02, (s01, s02) -> System.out.println("s01 - s02 = " + (s01 - s02)));
//        System.out.println("thenCommbineCf.get() = " + thenCommbineCf.get());
//      applyToEither方法-> 两个CompletionStage，谁执行返回的结果快，我就用那个CompletionStage的结果进行下一步的转化操作。
        CompletableFuture<Integer> acceptEitherCf = supplyFuture01.applyToEither(supplyFuture02, (s) -> s.intValue());
//      acceptEither方法-> 两个CompletionStage，谁执行返回的结果快，我就用那个CompletionStage的结果进行下一步的消耗操作
        CompletableFuture<Void> acceptEither = supplyFuture01.acceptEither(supplyFuture02, (s) -> System.out.println("s = " + s));
//      runAfterEither方法-> 两个CompletionStage，任何一个完成了都会执行下一步的操作（Runnable）
        supplyFuture01.runAfterEither(supplyFuture02, () -> System.out.println("runAfterEither."));
//      runAfterBoth-> 两个CompletionStage，都完成了计算才会执行下一步的操作（Runnable）
        supplyFuture01.runAfterBoth(supplyFuture02, () -> System.out.println("runAfterBoth."));
//      thenCompose方法-> thenCompose 方法允许你对两个 CompletionStage 进行流水线操作，第一个操作完成时，将其结果作为参数传递给第二个操作。
        CompletableFuture<Integer> thenCompose = supplyFuture01.thenCompose(s -> supplyFuture02);
        System.out.println(thenCompose.get());
    }
}
